//#define __FORCE_TRACE__
//#define __FORCE_DEBUG__
//#define __FORCE_INFO__
#define THIS_FILE "ia\fn_attack.sqf"
#include <tmf_constants.h>
#include <tmf_macros.h>

TRACE_1("Start",_this);
TMFPROFILERSTART;

private ["_group","_target_pos","_target_radius","_distance","_intervals","_i","_angle","_nx","_ny","_wp","_formation","_pos","_first_wp","_with_intermediate_points","_slope_dist","_route"];
DEFAULT_PARAM(_group,0,grpNull);
DEFAULT_PARAM(_target_pos,1,ARR_3(0,0,0));
DEFAULT_PARAM(_target_radius,2,200);
DEFAULT_PARAM(_with_intermediate_points,3,false);
DEFAULT_PARAM(_route,4,[]);

if (IS_STRING(_target_pos) && {IS_MARKER(_target_pos)}) then {
	_target_radius = ((markerSize _target_pos) select 0) max _target_radius;
	_target_pos = markerPos _target_pos;
};

if (AND_ALSO(AND_ALSO((!isnull _group && IS_ARRAY(_target_pos)),(count _target_pos == 3)),((_target_pos distance ARR_3(0,0,0)) > 0))) then {
	if (OR_ELSE(!(IS_SCALAR(_target_radius)),(_target_radius < 50))) then { _target_radius = 50 };
	if (IS_OBJECT(_group)) then {
		_group = group _group;
	};
	if (AND_ALSO(IS_GROUP(_group),(!isnull _group))) then {
		if (!isServer && !(local (leader _group))) exitWith { ERROR_1("Execution should be on server or local to the unit",_this); };
		_slope_dist = 5;
		
		_is_vec = false;
		// We add handlers which will impact the behavior of other units in the group.
		// If a member got killed, it will alert the others. If someone fires near them, it will alert the others.
		// Also, we check if there is at least one unit in a land vehicle (and it's not a static).
		{ 
			_x AddEventHandler ["firedNear", {private ["_grp"]; _grp = group (_this select 0); TRACE_1("FiredNear",_grp); _grp setspeedmode "NORMAL"; _grp setCombatMode "RED"; _grp setBehaviour "COMBAT";}];
			_x AddEventHandler ["handleDamage", {private ["_grp"]; _grp = group (_this select 0); TRACE_1("HandleDamage",_grp); _grp setspeedmode "NORMAL"; _grp setCombatMode "RED"; _grp setBehaviour "COMBAT"; _this select 2;}];
			if (vehicle _x != _x && ((vehicle _x) isKindOf "LandVehicle" && !(vehicle _x isKindOf "StaticWeapon"))) then { _is_vec = true; };
		} foreach units _group;
		
		if (!_is_vec) then { _slope_dist = -1 };
		if (!IS_ARRAY(_route)) then { _route = [] };
		
		_group setBehaviour "AWARE";
		_group setCombatMode "RED";
		_group allowFleeing 0;
		_group setSpeedMode "FULL";
		_group setFormation "STAG COLUMN";
				
		[_group] call FFUNC(deleteWaypoints);
		
		_distance = (_target_pos distance (leader _group));
		if (_with_intermediate_points) then {
			if (count _route > 0) then {
				// We used a predefined given route.
				TRACE_1("Adding Route",ARR_2(_group,_route));
				for "_i" from 0 to ((count _route) - 1) step 1 do {
					_pos = _route select _i;
					_wp = _group addWaypoint [_pos,20];
					_wp setWaypointBehaviour "AWARE";
					_wp setWaypointCombatMode "RED";
					_wp setWaypointCompletionRadius 150;
					_formation = ["STAG COLUMN", "WEDGE", "ECH LEFT", "ECH RIGHT", "VEE", "DIAMOND"] call FFUNC(arrayRandomVal);
					_wp setWaypointFormation _formation;
					_wp setWaypointSpeed "FULL";
					_wp setWaypointType "MOVE";
				};
			} else {
				// Add waypoints along the way to get the group to destination but allow to stop it if it encounters resistance.
				if (_distance > 200) then {
					_intervals = floor(_distance / 200) - 1;
					if (_intervals > 0) then {
						TRACE_1("Adding Intervals",ARR_2(_group,_intervals));
						_nx = (getPos (leader _group)) select 0;
						_ny = (getPos (leader _group)) select 1;
						for "_i" from 0 to ((_intervals) - 1) step 1 do {
							_angle = [[_nx,_ny,0],_target_pos] call FFUNC(dirTo);
							_pos = [[_nx,_ny,0], _angle, 180, 35, _slope_dist] call FFUNC(getRanDirPoint);
							_nx = _nx + (sin _angle) * 200;
							_ny = _ny + (cos _angle) * 200;
							_wp = _group addWaypoint [[_nx, _ny, 0],20];
							_wp setWaypointBehaviour "AWARE";
							_wp setWaypointCombatMode "RED";
							_wp setWaypointCompletionRadius 150;
							_formation = ["STAG COLUMN", "WEDGE", "ECH LEFT", "ECH RIGHT", "VEE", "DIAMOND"] call FFUNC(arrayRandomVal);
							_wp setWaypointFormation _formation;
							_wp setWaypointSpeed "FULL";
							_wp setWaypointType "MOVE";
						};
					};
				};
			};
		};
		// We add an entry point into the zone, it will be a bit outside of the zone, somewhat in the direction the group is coming from
		// And it will trigger a stealth movement to get into the zone.
		_angle = [(getPos (leader _group)),_target_pos] call FFUNC(dirTo);
		_pos = [_target_pos, _angle, (_target_radius + ((_target_radius / 2) min 120)), 45, _slope_dist] call FFUNC(getRanDirPoint);
		if (count _pos <= 0) then {
			_pos = [_target_pos,120,_angle] call FFUNC(relPos3D);
			_pos set [2,0];
		};
		_wp = _group addWaypoint [_pos,10];
		_wp setWaypointBehaviour "AWARE";
		_wp setWaypointCombatMode "RED";
		_wp setWaypointCompletionRadius (_target_radius max 120);
		_formation = ["STAG COLUMN", "WEDGE", "ECH LEFT", "ECH RIGHT", "VEE", "DIAMOND"] call FFUNC(arrayRandomVal);
		_wp setWaypointFormation _formation;
		_wp setWaypointSpeed "FULL";
		_wp setWaypointType "MOVE";
		// I've splitted it into 2 waypoints because the dumb IAs tend to get slow too early...
		_pos = [_target_pos, _angle, (_target_radius + ((_target_radius / 2.2) min 100)), 45, _slope_dist] call FFUNC(getRanDirPoint);
		if (count _pos <= 0) then {
			_pos = [_target_pos,100,_angle] call FFUNC(relPos3D);
			_pos set [2,0];
		};
		_wp = _group addWaypoint [_pos,10];
		_wp setWaypointBehaviour "COMBAT";
		_wp setWaypointCombatMode "RED";
		_wp setWaypointCompletionRadius (_target_radius max 120);
		_formation = ["STAG COLUMN", "WEDGE", "ECH LEFT", "ECH RIGHT", "VEE", "DIAMOND"] call FFUNC(arrayRandomVal);
		_wp setWaypointFormation _formation;
		_wp setWaypointSpeed "LIMITED";
		_wp setWaypointType "MOVE";
		
		_intervals = (floor(_target_radius / 50) max 1);
		_first_wp = count (waypoints _group);
		TRACE_1("Adding SADs",ARR_3(_group,_intervals,_first_wp));
		// Add Search And Destroy Waypoints
		for "_i" from 0 to ((_intervals) - 1) step 1 do {
			_pos = [_target_pos,(_target_radius * 0.7),0,_slope_dist] call FFUNC(getRanPointCircle);
			_wp = _group addWaypoint [_pos,(_target_radius / 4)];
			_wp setWaypointBehaviour "COMBAT";
			_wp setWaypointCombatMode "RED";
			_wp setWaypointCompletionRadius _target_radius;
			_formation = ["STAG COLUMN", "WEDGE", "ECH LEFT", "ECH RIGHT", "VEE", "DIAMOND"] call FFUNC(arrayRandomVal);
			_wp setWaypointFormation _formation;
			_wp setWaypointSpeed "LIMITED";
			_wp setWaypointType "SAD";
			_wp setWaypointTimeout [(240 / _intervals),(360 / _intervals),(480 / _intervals)];
		};
		// Add a cycle waypoint to keep running through the waypoints
		_pos = waypointPosition [_group, _first_wp];
		_wp = _group addWaypoint [_pos,10];
		_wp setWaypointBehaviour "COMBAT";
		_wp setWaypointCombatMode "RED";
		_wp setWaypointCompletionRadius _target_radius;
		_formation = ["STAG COLUMN", "WEDGE", "ECH LEFT", "ECH RIGHT", "VEE", "DIAMOND"] call FFUNC(arrayRandomVal);
		_wp setWaypointFormation _formation;
		_wp setWaypointSpeed "LIMITED";
		_wp setWaypointType "CYCLE";
		
		_group setCurrentWaypoint [_group, 0];
		_pos = waypointPosition [_group, 0];
		{ if (_x != (leader _group)) then { _x doFollow (leader _group); };
		} foreach units _group;
		(leader _group) doMove _pos;
		
		[_group,_target_pos,_target_radius] spawn {
			private ["_group","_target_pos","_target_radius","_last_pos","_wp_idx","_pos","_behavior"];
			_group = _this select 0;
			_target_pos = _this select 1;
			_target_radius = _this select 2;
			_last_pos = getPos (leader _group);
			while { !isnull _group && (((leader _group) distance _target_pos) > (_target_radius * 1.3)) && alive (leader _group) } do {
				if (((leader _group) distance _last_pos) <= 20) then {
					_wp_idx = currentWaypoint _group;
					_behavior = behaviour (leader _group);
					if (!(_behavior in ["COMBAT","STEALTH"])) then {
						_pos = waypointPosition [_group, _wp_idx];
						(leader _group) doMove _pos;
					};
				};
				{ if ((_x distance (leader _group)) > 50) then { _x doFollow (leader _group); };
				} foreach units _group;
				_last_pos = getPos (leader _group);
				sleep (30 + (random 10));
			};
		};
		TRACE_1("Waypoints",ARR_2(_group,(count waypoints _group)));
		_group setVariable ["attack",_target_pos];
		if (IS_DEBUG && !isNull _group && !isNull (leader _group)) then {
			private ["_marker_pos","_marker_name","_marker"];
			_marker_pos = getPos (leader _group);
			_marker_name = Format["%1%2",str(_group),0];
			_marker = createMarker[_marker_name,[_marker_pos select 0,_marker_pos select 1]];
			_marker setMarkerShape "ICON";
			_marker_name setMarkerType "mil_dot";
			_marker_name setMarkerColor "ColorRed";
			_marker_name setMarkerText Format["%1",str(_group)];
			_marker_name setMarkerAlpha 0.5;
			if ((count waypoints _group) > 0) then {
				_i = 1;
				private ["_marker","_marker_name","_marker_pos"];
				{
					if (!((waypointType _x) in ["CYCLE"])) then {
						_marker_pos = waypointPosition _x;
						_marker_name = Format["%1%2",str(_marker_pos),_i];
						_marker = createMarker[_marker_name,[_marker_pos select 0,_marker_pos select 1]];
						_marker setMarkerShape "ICON";
						_marker_name setMarkerType "mil_triangle";
						_marker_name setMarkerColor "ColorRed";
						if (!((waypointType _x) in ["SAD","DESTROY","HOLD"])) then {
							_marker_name setMarkerColor "ColorBrown";
						};
						_marker_name setMarkerText Format["A-%1-%2",str(_group), str(_i)];
						_marker_name setMarkerAlpha 0.5;
						DEBUG_1("WP,Type,Form,Pos,",ARR_5(_group,(currentWaypoint _group),waypointType _x,waypointFormation _x,waypointPosition _x));
					};
					_i = _i + 1;
				} foreach waypoints _group;
			};
		};
	} else {
		ERROR("Invalid Group");
	};
} else {
	ERROR("Invalid Parameters");
};

TRACE("End");
TMFPROFILERSTOP;